package com.wangshb.agent.study.javassist;


import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.*;
import net.bytebuddy.description.method.MethodDescription;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * 打印运行中的信息
 * @author wangsb12
 */
public class PrintRunInfoStudy extends ClassLoader{

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = pool.get(Test.class.getName());
        ctClass.replaceClassName("Test", "Test01");
        String clazzName = ctClass.getName();


        // 获取方法名称
        CtMethod ctMethod = ctClass.getDeclaredMethod("strToInt");
        String methodName = ctMethod.getName();


        MethodInfo methodInfo = ctMethod.getMethodInfo();

        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        // 获取入参
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        CtClass[] parameterTypes = ctMethod.getParameterTypes();

        // 是否静态方法
        boolean isStatic = (methodInfo.getAccessFlags() & AccessFlag.STATIC) != 0;
        int parameterSize = isStatic ? attr.tableLength() : attr.tableLength() - 1;

        ArrayList<String> parameterNameList = new ArrayList<>();
        ArrayList<String> parameterTypeList = new ArrayList<>();

        StringBuilder parameters = new StringBuilder();
        for (int i = 0; i < parameterSize; i++) {
            // 静态类型去掉第一个this参数
            parameterNameList.add(attr.variableName(i + (isStatic ? 0 : 1)));
            parameterTypeList.add(parameterTypes[i].getName());
            if (i + 1 == parameterSize) {
                parameters.append("$").append(i + 1);
            } else {
                parameters.append("$").append(i + 1).append(",");
            }
        }

        // 获取返回值
        CtClass returnType = ctMethod.getReturnType();
        String returnTypeName = returnType.getName();


        int idx = Monitor.generateMethodId(clazzName, methodName, parameterNameList, parameterTypeList, returnTypeName);

        // 定义一个 long 属性，并通过 insertBefore 插入到方法内容的开始处
        // 定义属性
        ctMethod.addLocalVariable("startNanos", CtClass.longType);
        ctMethod.addLocalVariable("parameterValues", pool.get(Object[].class.getName()));

        // 方法前加强
        ctMethod.insertBefore("{ startNanos = System.nanoTime(); parameterValues = new Object[]{" + parameters.toString() + "}; }");


        // 如果返回类型非对象类型，$_ 需要进行类型转换
        ctMethod.insertAfter("{com.wangshb.agent.study.javassist.Monitor.point(" + idx + ", startNanos, parameterValues, $_);}", false);

        // 方法；添加TryCatch, 添加异常捕获
        ctMethod.addCatch("{ com.wangshb.agent.study.javassist.Monitor.point(" + idx + ", $e); throw $e; }", ClassPool.getDefault().get("java.lang.Exception"));

        ctClass.writeFile();

        // 测试调用
        byte[] bytes = ctClass.toBytecode();

        Class<?> clazzNew = new PrintRunInfoStudy().defineClass("com.wangshb.agent.study.javassist.Test", bytes, 0, bytes.length);

        // 反射获取 main 方法
        Method method = clazzNew.getMethod("strToInt", String.class, String.class);
        Object obj_01 = method.invoke(clazzNew.newInstance(), "1", "2");
        System.out.println("正确入参：" + obj_01);

        Object obj_02 = method.invoke(clazzNew.newInstance(), "a", "b");
        System.out.println("异常入参：" + obj_02);
    }


}
